//============================================================================
// Name        : GA.cpp
// Author      : Andrey Bicalho Santos e Clayson Sandro Celes
// Version     :
// Copyright   : Your copyright notice
// Description : Genetic Algorithm in C++, Ansi-style
//============================================================================

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <cstring>
#include "GA.h"
#include "MazeProblem.h"

using namespace std;


Point dim_solution_map,dim_labirinto,dim_reduced_labirinto;
int **Maze_problem(int **labirinto,int number_genes,int number_individuals,int number_ages,int m_rateX,int m_rateY,int k_tournament,int k_elitism,int max_min_type,int criterio_convergencia);

//-------------------------------------------------------------------------------------
int main(int argc, char** argv) {

	/*
	 * Prisoner's Problem
	 * */
	FILE *arquivo;
	char endereco[30];
	int number_genes,number_individuals,number_ages,m_rateX,m_rateY,
	    k_tournament,k_elitism,max_min_type,criterio_convergencia;
	if(argc < 2){
		cout << "Insira o labirinto: (ex: nome_do_arquivo.txt)" << endl; cin >> endereco;
		cout << "\nInsira o numero de genes: " << endl; cin >> number_genes;
		cout << "\nInsira o numero de individuos; " << endl; cin >> number_individuals;
		cout << "\nInsira o numero de geracoes: " << endl; cin >> number_ages;
		cout << "\nInsira parametro 1 da taxa de mutacao: " << endl; cin >> m_rateX;
		cout << "\nInsira parametro 2 da taxa de mutacao: " << endl; cin >> m_rateY;
		cout << "\nInsira parametro K do torneio: " << endl; cin >> k_tournament;
		cout << "\nInsira parametro K do elitismo: " << endl; cin >> k_elitism;
		cout << "\nInsira o tipo de problema (1- maximization e 0- minimization): " << endl; cin >> max_min_type;
		cout << "\nInsira o criterio de convergencia: " << endl; cin >> criterio_convergencia;
	}
	else{
		strcpy(endereco,argv[1]);
		number_genes = atoi(argv[2]);
		number_individuals = atoi(argv[3]);
		number_ages = atoi(argv[4]);
		m_rateX = atoi(argv[5]);
		m_rateY = atoi(argv[6]);
		k_tournament = atoi(argv[7]);
		k_elitism = atoi(argv[8]);
		max_min_type = atoi(argv[9]);
		criterio_convergencia = atoi(argv[10]);
	}

	/*
	//teste 1 - criando populacao com individuos de tamanhos diferentes...
	Individual *population;
	population = new Individual[300];
	int r;
	for(int i=0;i<300;i++){
		r = rand() % 100;
		if(r == 0) r =1;
		population[i].setNumGenes(r);
		population[i].Initialize();
	}

	//for(int i=0;i<300;i++)
		//population[i].printIndividual();



	//teste 2 - crescendo e diminuindo o tamanho dos individuos
	population[10].setNumGenes(population[0].getNumGenes());
	population[10].ReInitialize();
	population[10] = population[0];


	for(int i=0;i<300;i++)
		population[i].printIndividual();

*/





	arquivo = fopen(endereco,"r");
	if(arquivo == NULL){ cout << "erro ao abrir arquivo de entrada" << endl; return 0;}

	string *mapa; int n_linhas;
	mapa = load_map(arquivo,&n_linhas);

	int **solution_map,**labirinto,**reduced_labirinto;

	labirinto = str_map2bin_map(mapa,n_linhas,&dim_labirinto);
	//printMapa(labirinto,dim_labirinto); pera();
	reduced_labirinto = get_reduced_map(labirinto,dim_labirinto,&dim_reduced_labirinto);
	//printMapa(reduced_labirinto,dim_reduced_labirinto); pera();
	reduced_labirinto = preProcessing(reduced_labirinto,dim_reduced_labirinto);
	//printMapa(reduced_labirinto,dim_reduced_labirinto); pera();

	dim_solution_map = dim_reduced_labirinto;


	solution_map = Maze_problem(reduced_labirinto,number_genes,number_individuals,number_ages,m_rateX,m_rateY,
			               k_tournament,k_elitism,max_min_type,criterio_convergencia);

	//string *new_mapa; new_mapa = build_final_map(labirinto,dim_labirinto,solution_map,dim_solution_map);

	//printMapa(reduced_labirinto,dim_reduced_labirinto);
	printf("\n");
	printMapa(solution_map,dim_solution_map);

	fclose(arquivo);

	//liberando memoria...
	if(mapa) delete[] mapa;

	for(int i=0;i<dim_solution_map.x;i++){
		delete[] solution_map[i];
		delete[] reduced_labirinto[i];
	}
	delete[] solution_map;
	delete[] reduced_labirinto;

	for(int i=0;i<dim_labirinto.x;i++)
		delete[] labirinto[i];
	delete[] labirinto;


	cout << "\nit works!\n";
	return 0;
}

//-------------------------------------------------------------------------------------
int **Maze_problem(int **reduced_labirinto,int number_genes,int number_individuals,int number_ages,int m_rateX,int m_rateY,int k_tournament,int k_elitism,int max_min_type,int criterio_convergencia){
	cout << "Prisoner's problem\n\n" << endl;

		int **solution_map,**temp_map;

		solution_map = copy_map(reduced_labirinto,dim_solution_map);
		temp_map = copy_map(reduced_labirinto,dim_solution_map);

		int free_space = get_number_of_valid_space(solution_map,dim_solution_map);
		//printf("\n%d\n",free_space); pera();

		/*
		 * starting GA part...
		 * */

		Point start = FindStartPoint(solution_map,dim_solution_map),
			  end = FindEndPoint(solution_map,dim_solution_map);


		number_genes = free_space;
		GA *prisonerProblem;
		prisonerProblem = new GA(number_ages,number_genes,number_individuals,
				                 m_rateX,m_rateY,k_tournament,k_elitism,1);
		Individual *population;
		population = new Individual[number_individuals];
		for(int i=0;i<number_individuals;i++){
			population[i].setNumGenes(number_genes);
			population[i].Initialize();
			InitializeIndividualForMazeProblem(population,i,solution_map,start,dim_solution_map);
		}


		int index_melhor,goodgenes = 0;

		do{

			//prisonerProblem->printPopulation(population);
			/*
			for(int i=0;i<prisonerProblem->getNumIndividuals();i++){
				clone(reduced_labirinto,temp_map,dim_solution_map);
				printf("\nIndividuo %d  #Genes: %d",i,population[i].getNumGenes());
				population[i].printIndividual();
				temp_map = InsertSolution(population,i,temp_map,dim_solution_map);
				printMapa(temp_map,dim_solution_map); pera();
			}
			*/


			for(int i=0;i<prisonerProblem->getNumIndividuals();i++)
				population[i].setFitness(fitness_MazeProblem(population,i,number_individuals,solution_map,start,end,dim_solution_map,free_space));

			//index_melhor = prisonerProblem->getHighestFitnessIndividualIndex(population);
			index_melhor = prisonerProblem->getLowestFitnessIndividualIndex(population);
			printf("\nAge: %d",prisonerProblem->getCurrentAge());
			printf("\tFitness Average: %f",prisonerProblem->getAverageFitness(population));
			printf("\tBest Individual: %d  Fitness: %f  #Genes: %d",index_melhor,population[index_melhor].getFitness(),population[index_melhor].getNumGenes());



			//population[index_melhor].printIndividual();
			clone(reduced_labirinto,temp_map,dim_solution_map);
			temp_map = InsertSolution(population,index_melhor,temp_map,dim_solution_map);
			printMapa(temp_map,dim_solution_map); //pera();




			population = prisonerProblem->Run(population);

		}while((prisonerProblem->getCurrentAge() < prisonerProblem->getAge()) );//&& (prisonerProblem->getAverageFitness(population) < (population[prisonerProblem->getHighestFitnessIndividualIndex(population)].getFitness()) - criterio_convergencia));

		for(int i=0;i<prisonerProblem->getNumIndividuals();i++)
			population[i].setFitness(fitness_MazeProblem(population,i,number_individuals,solution_map,start,end,dim_solution_map,free_space));


		//index_melhor = prisonerProblem->getHighestFitnessIndividualIndex(population);
		index_melhor = prisonerProblem->getLowestFitnessIndividualIndex(population);
		printf("\nGA ended\nThe Best Individual is: %d",index_melhor);
		printf("\nAge: %d",prisonerProblem->getCurrentAge());
		printf("\tFitness Average: %f",prisonerProblem->getAverageFitness(population));
		printf("\tBest Individual: %d  Fitness: %f  #Genes: %d",index_melhor,population[index_melhor].getFitness(),population[index_melhor].getNumGenes());
		population[index_melhor].printIndividual();

		solution_map = InsertSolution(population,index_melhor,solution_map,dim_solution_map);
		goodgenes = count_good_genes(solution_map,reduced_labirinto,dim_solution_map);
		printf("\n\nNumber of good Genes: %d, Number of valid pass: %d\n",goodgenes,goodgenes/2);
		printf("Number of invalid pass: %d\n",count_erros(solution_map,reduced_labirinto,dim_solution_map));

		for(int i=0;i<dim_solution_map.x;i++)
			delete[] temp_map[i];
		delete[] temp_map;


	return solution_map;
}
//-------------------------------------------------------------------------------------
